package registry

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
)

const ServerPort = ":3000"
const ServiceUrl = "http://localhost" + ServerPort + "/services"

type registry struct {
	registrations []Registration
	mutex         *sync.RWMutex
}

func (r *registry) add(registration Registration) error {
	r.mutex.Lock()
	r.registrations = append(r.registrations, registration)
	err := r.sendRequiredServices(registration)
	r.notify(patch{
		Added: []patchEntry{
			{
				Name: registration.ServiceName,
				Url:  registration.ServiceUrl,
			},
		},
	})
	r.mutex.Unlock()
	return err
}

func (r *registry) notify(fullPatch patch) {
	// r.mutex.RLock()
	// defer r.mutex.RUnlock()
	for _, registration := range r.registrations {
		go func(registration Registration) {
			for _, service := range registration.RequireServices {
				p := patch{
					Added:   []patchEntry{},
					Removed: []patchEntry{},
				}

				sendUpdate := false
				for _, added := range fullPatch.Added {
					if added.Name == service {
						p.Added = append(p.Added, added)
						sendUpdate = true
					}
				}

				for _, removed := range fullPatch.Removed {
					if removed.Name == service {
						p.Removed = append(p.Removed, removed)
						sendUpdate = true
					}
				}

				if sendUpdate {
					err := r.sendPatch(p, registration.ServiceUrl)
					if err != nil {
						log.Println(err)
						return
					}
				}
			}
		}(registration)
	}
}

func (r *registry) sendRequiredServices(registration Registration) error {
	// 这里有点问题，我这里加锁会导致不能释放，其他请求进不来
	// r.mutex.Lock()
	// defer r.mutex.Unlock()
	var p patch
	for _, serviceReg := range r.registrations {
		for _, reqService := range registration.RequireServices {
			if serviceReg.ServiceName == reqService {
				p.Added = append(p.Added, patchEntry{
					Name: serviceReg.ServiceName,
					Url:  serviceReg.ServiceUrl,
				})
			}
		}
	}
	fmt.Println(p)
	err := r.sendPatch(p, registration.ServiceUpdateUrl)
	if err != nil {
		return err
	}
	return nil
}

func (r *registry) sendPatch(p patch, url string) error {
	d, err := json.Marshal(p)
	if err != nil {
		return err
	}
	_, err = http.Post(url, "application/json", bytes.NewBuffer(d))
	if err != nil {
		return err
	}
	return nil
}

func (r *registry) remove(url string) error {
	for i := range r.registrations {
		if r.registrations[i].ServiceUrl == url {
			r.notify(patch{Removed: []patchEntry{
				{
					Name: r.registrations[i].ServiceName,
					Url:  r.registrations[i].ServiceUrl,
				},
			}})
			r.mutex.Lock()
			r.registrations = append(r.registrations[:i], r.registrations[i+1:]...)
			r.mutex.Unlock()
			return nil
		}
	}

	return fmt.Errorf("service at URL %s not found", url)
}

var reg = registry{
	registrations: make([]Registration, 0),
	mutex:         new(sync.RWMutex),
}

type Service struct{}

func (s Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Println("Request received.")
	switch r.Method {
	case http.MethodPost:
		dec := json.NewDecoder(r.Body)
		var registration Registration
		err := dec.Decode(&registration)
		if err != nil {
			log.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		log.Printf("Adding Service: %v with URL: %s", registration.ServiceName, registration.ServiceUrl)
		err = reg.add(registration)
		if err != nil {
			log.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	case http.MethodDelete:
		payload, err := io.ReadAll(r.Body)
		if err != nil {
			log.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		url := string(payload)
		log.Printf("Removing Service at URL: %s\n", url)
		err = reg.remove(url)
		if err != nil {
			log.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
}
